package com.succez.server.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.HashMap;
import com.succez.server.servlet.http.WebApp;
import com.succez.server.utils.CloseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程执行调度单元，解析http请求，产生request，根据路径进行匹配servlet，进行servlet实例化，转交对应servlet进行处理
 */
public class ConnectionHandleTask implements Runnable {
    /**
     * socket连接，初始化后不可更改
     */
    private final Socket connection;

    /**
     * Response对象
     */
    private Response response;

    /**
     * Request对象
     */
    private Request request;

    /**
     * 日志记录
     */
    private static Logger logger = LoggerFactory.getLogger(ConnectionHandleTask.class);

    public ConnectionHandleTask(Socket connection) {
        this.connection = connection;
        response = new Response(connection);
        request = new Request();
    }

    @Override
    public void run() {
        if (parse()) {
            // 如果头部信息解析正常，则进行服务的分发
            String servletName = WebApp.servletContext.getServletName(request.getUrl());
            if (servletName != null) {
                // 映射到了具体的Servlet
                Servlet servletObj = WebApp.servletContext.getServlet(servletName);
                servletObj.service(response, request);
            } else {
                WebApp.servletContext.getServlet("StaticResource").service(response, request);
            }
        } else {
            // 头部解析错误
            response.setHeadInfo("content-type", WebApp.contentTypeMappings.get("html"));
            String text = "";
            if (response.getCode() == 404) {
                text = "源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示。";
            } else if (response.getCode() == 400) {
                text = "请求的参数不符合服务器规范。";
            } else if (response.getCode() == 500) {
                text = "服务器内部处理发生了错误。";
            }
            String bodytext = response.generateError(response.getStatusLine().toString(),
                    WebApp.statusCodeMappings.get(response.getCode()), "报告", response.getFailText(), text);
            response.setBodyText(bodytext);
            response.pushToClient();
        }
        CloseUtil.closeSocket(connection);
    }

    /**
     * 
     * @return 解析成功返回true
     */
    private boolean parse() {
        InputStream input = null;
        boolean isSuccess = true;
        try {
            input = connection.getInputStream();
        } catch (IOException e) {
            response.setFailText(e.getMessage());
            isSuccess = false;
        }
        if (!isSuccess) {
            response.setStatusLine(500);
            return isSuccess;
        }
        // 如果input获取正常，则进行下一步的解析动作
        try {
            // 如果该次一直不能获取数据，会进行重试，默认此时10000000次,最长耗时6s
            int length = input.available();
            int count = 0;
            long start = System.currentTimeMillis();
            while (length == 0) {
                count++;
                length = input.available();
                if (++count >= Consts.RETRY_COUNT) {
                    length = -1;
                    logger.error("连接异常,重试耗时:" + (System.currentTimeMillis() - start) + "ms", "");
                }
            }
            if (count != 0 && length > 0) {
                logger.info("此次连接有延迟" + "重试了" + count + "次;耗时:" + (System.currentTimeMillis() - start) + "ms", "");
            }
            if (length < 0)
                length = 0;
            byte[] data = new byte[length];
            int len = input.read(data);
            String contentText = new String(data, 0, len);
            isSuccess = parseContentText(contentText);
        } catch (IOException e) {
            isSuccess = false;
            response.setStatusLine(500);
            response.setFailText(e.getMessage());
        }
        return isSuccess;
    }

    /**
     * 解析报文
     */
    private boolean parseContentText(String contentText) {
        boolean isSuccess = true;
        String[] lines = contentText.split(Consts.CRLF);
        if (lines.length <= 1) {
            isSuccess = false;
            response.setStatusLine(400);
            response.setFailText("请求报文至少需要包含请求行和头部信息");
            return isSuccess;
        }
        String[] statusLine = lines[0].split(" ");
        if (statusLine.length != 3) {
            isSuccess = false;
            response.setStatusLine(400);
            response.setFailText("请求行格式错误");
            return isSuccess;
        }
        request.setMethod(statusLine[0]);
        request.setProtocol(statusLine[2]);
        try {
            request.setRawUrl(URLDecoder.decode(statusLine[1], "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            logger.error("对传入的中文参数解码失败", e.getMessage());
        }
        if (statusLine[0].equals(Consts.METHOD_GET)) {
            isSuccess = parseGet(contentText);
        } else if (statusLine[0].equals(Consts.METHOD_POST)) {
            isSuccess = parsePost(contentText);
        }
        return isSuccess;
    }

    /**
     * 解析post数据，需要根据content-length和content-type去决定解析的方式
     */
    private boolean parsePost(String contentText) {
        boolean isSuccess = true;
        boolean isUrlEncoded = contentText.contains("application/x-www-form-urlencoded");
        boolean isJson = contentText.contains("application/json");
        isSuccess = parseGet(contentText.substring(0, contentText.lastIndexOf(Consts.CRLF)));
        // 只有状态行和头部解析成功，才会对body进行解析
        if (isSuccess) {
            String bodyParams = contentText.substring(contentText.lastIndexOf(Consts.CRLF), contentText.length());
            // 添加对参数的解析
            if (isUrlEncoded) {
                String[] params = bodyParams.split("&");
                int index = 0;
                for (String param : params) {
                    if (param.contains("=")) {
                        index = param.indexOf("=");
                        request.getQueryParams().put(param.substring(0, index).trim(),
                                param.substring(index + 1, param.length()).trim());
                    }
                }
            } else if (isJson) {
                if (!bodyParams.equals("")) {
                    request.getQueryParams().put("jsonObject", bodyParams.trim());
                }
            }
        }
        return isSuccess;
    }

    /**
     * 没有body，只需要简单的解析url里的参数即可
     */
    private boolean parseGet(String contentText) {
        boolean isSuccess = true;
        String[] lines = contentText.split(Consts.CRLF);
        HashMap<String, String> headInfo = new HashMap<>();
        HashMap<String, String> queryParams = new HashMap<>();
        // 设置头部信息
        for (int i = 1; i < lines.length; i++) {
            if (lines[i].contains(":")) {
                if (lines[i] == Consts.CRLF) {
                    break;
                }
                headInfo.put(lines[i].substring(0, lines[i].indexOf(":")).trim(),
                        lines[i].substring(lines[i].indexOf(":") + 1, lines[i].length()).trim());
            }

        }
        // 进行查询参数分解和url设置
        if (request.getRawUrl().contains("?")) {
            try {
                request.setUrl(request.getRawUrl().substring(0, request.getRawUrl().indexOf("?")).trim());
                String param = request.getRawUrl()
                        .substring(request.getRawUrl().indexOf("?") + 1, request.getRawUrl().length()).trim();
                String[] params = param.split("&");
                for (int i = 0; i < params.length; i++) {
                    int index = params[i].indexOf("=");
                    queryParams.put(params[i].substring(0, index).trim(),
                            params[i].substring(index + 1, params[i].length()).trim());
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                isSuccess = false;
                response.setStatusLine(400);
                response.setFailText("查询参数设置错误");
                return isSuccess;
            }

        } else {
            request.setUrl(request.getRawUrl());
        }
        if ("/".equals(request.getUrl())) {
            request.setUrl("/html/index.html");
            request.setRawUrl("/html/index.html");
        }
        request.setHeadInfo(headInfo);
        request.setQueryParams(queryParams);
        return isSuccess;
    }
}